
#include <cassert>

#include "Core\RenderTarget.h"
#include "Math\Vector4.h"


RenderTarget::RenderTarget(CONST Size& size,
						   DXGI_FORMAT format,
						   UINT flags,
						   UINT max_mip_level,
						   UINT mip_levels)
	: m_binded(FALSE),
	  m_render_target_texture(NULL),
	  m_depth_stencil_view(NULL),
	  m_render_target_view(NULL),
	  m_shader_resource_view(NULL)
{
	// Initialize texture 2D description.
	ZeroMemory(&m_tex2d_desc, sizeof(m_tex2d_desc));
	
	m_tex2d_desc.Width = size.GetWidth();
	m_tex2d_desc.Height = size.GetHeight();

	// We assume that the render target doesn't have higher mip levels that the resource can't use.
	m_tex2d_desc.MipLevels = max_mip_level + mip_levels; 

	m_tex2d_desc.ArraySize = 1;
	m_tex2d_desc.Format = format;
	m_tex2d_desc.SampleDesc.Count = 1;
	m_tex2d_desc.Usage = D3D11_USAGE_DEFAULT;
	m_tex2d_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	m_tex2d_desc.CPUAccessFlags = 0;
	m_tex2d_desc.MiscFlags = flags;

	// Initialize render target view description.
	ZeroMemory(&m_rtv_desc, sizeof(m_rtv_desc));

	m_rtv_desc.Format = format;
	m_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	m_rtv_desc.Texture2D.MipSlice = 0;

	// Initialize shader resource view description.
	ZeroMemory(&m_srv_desc, sizeof(m_srv_desc));

	m_srv_desc.Format = format;
	m_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	m_srv_desc.Texture2D.MostDetailedMip = max_mip_level;
	m_srv_desc.Texture2D.MipLevels = -1;
}


RenderTarget::~RenderTarget()
{
	if(IsBinded())
		Unbind();
}


BOOL RenderTarget::IsBinded() CONST
{
	return m_binded;
}


BOOL RenderTarget::Bind()
{
	assert(!m_binded);
	assert(!m_render_target_texture);
	assert(!m_render_target_view);
	assert(!m_shader_resource_view);

	HRESULT result = E_FAIL;

	// Create the texture 2D.
	result = GetDevice()->CreateTexture2D(&m_tex2d_desc, NULL, &m_render_target_texture);
	assert(SUCCEEDED(result));

	// Create render target view.
	result = GetDevice()->CreateRenderTargetView(m_render_target_texture, &m_rtv_desc, &m_render_target_view);
	assert(SUCCEEDED(result));

	// Create the shader resource view.
	result = GetDevice()->CreateShaderResourceView(m_render_target_texture, &m_srv_desc, &m_shader_resource_view);
	assert(SUCCEEDED(result));

	// Change state to binded.
	m_binded = TRUE;

	return TRUE;
}


VOID RenderTarget::Unbind()
{
	assert(m_binded);
	assert(m_render_target_texture);
	assert(m_render_target_view);
	assert(m_shader_resource_view);

	m_render_target_texture->Release();
	m_render_target_texture = NULL;

	m_render_target_view->Release();
	m_render_target_view = NULL;

	m_shader_resource_view->Release();
	m_shader_resource_view = NULL;

	m_depth_stencil_view = NULL;

	// Change state to unbinded.
	m_binded = FALSE;
}


Size RenderTarget::GetSize() CONST
{
	return Size(m_tex2d_desc.Width, m_tex2d_desc.Height);
}


ID3D11RenderTargetView* RenderTarget::GetRenderTarget() CONST
{
	return m_render_target_view;
}


ID3D11ShaderResourceView* RenderTarget::GetResource() CONST
{
	assert(m_binded);
	assert(m_render_target_view);

	return m_shader_resource_view;
}


VOID RenderTarget::ClearRenderTarget(CONST Vector4& color)
{
	assert(m_binded);
	assert(m_render_target_texture);
	assert(m_render_target_view);
	assert(m_shader_resource_view);

	FLOAT rgba[] = {color.GetX(), color.GetY(), color.GetZ(), color.GetW()};

	GetContext()->ClearRenderTargetView(m_render_target_view, rgba);
}


VOID RenderTarget::ClearRenderTarget(FLOAT r, FLOAT g, FLOAT b, FLOAT a)
{
	assert(m_binded);
	assert(m_render_target_texture);
	assert(m_render_target_view);
	assert(m_shader_resource_view);

	FLOAT rgba[] = {r, g, b, a};

	GetContext()->ClearRenderTargetView(m_render_target_view, rgba);
}